package com.franklin.java.action.sql.entity.ui;

import com.flychord.jdk.util.matchcase.EntryCase;
import com.flychord.jdk.util.matchcase.TupleCase;
import com.flychord.jdk.util.tuple.Tuple2;
import com.flychord.jdk.util.tuple.Tuple3;
import com.franklin.java.api.mybatis.dom.franklin.MyMapper;
import com.franklin.java.api.mybatis.dom.utils.MapperUtils;
import com.franklin.java.configuration.sql.completion.TableNamePrefixState;
import com.franklin.java.entity.sql.ColumnInfo;
import com.franklin.java.icon.MybatisIcons;
import com.franklin.java.service.database.AbstractDatabaseHandler;
import com.franklin.java.utils.ModuleUtils;
import com.franklin.java.utils.NotificationUtils;
import com.franklin.java.utils.PsiUtils;
import com.franklin.java.utils.XmlTagUtils;
import com.franklin.plugins.common.utils.DataCheckUtils;
import com.franklin.plugins.common.utils.StringHelper;
import com.google.common.collect.Lists;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.NlsSafe;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.xml.XmlTag;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.flychord.jdk.util.tuple.TupleAble.tuple;

/**
 * @author Franklin
 * @since 2021/9/13 10:38
 */
public class ResultMapNode extends ResultMapChildNode {

    protected List<ColumnInfo> columnInfoList;
    protected PsiElement sqlActionElement;

    private String id = "id";
    private String type = "type";

    protected ResultMapNode(
        List<ColumnInfo> columnInfoList,
        PsiElement sqlActionElement,
        String type
    ) {
        super(null);
        this.columnInfoList = columnInfoList;
        this.sqlActionElement = sqlActionElement;
        XmlTag selectTag = (XmlTag) sqlActionElement;
        String resultMapId = selectTag.getAttributeValue("resultMap");
        if (DataCheckUtils.isNotEmpty(resultMapId)){
            this.id = resultMapId;
            XmlTag mapperTag = selectTag.getParentTag();
            XmlTag resultMapTag = XmlTagUtils.findChildTag(mapperTag, "resultMap", "id", resultMapId);
            if (Objects.nonNull(resultMapTag)){
                String typeFromTag = resultMapTag.getAttributeValue("type");
                if (DataCheckUtils.isNotEmpty(typeFromTag)){
                    this.type = typeFromTag;
                }
            }
        }

        if (DataCheckUtils.isNotEmpty(type)){
            this.type = type;
        }

        setIcon(MybatisIcons.logo);
        setProperties();
        init();
    }

    protected void init() {
        Module module = ModuleUtil.findModuleForPsiElement(sqlActionElement);
        String[] prefixes = TableNamePrefixState.getPrefix(project).split(";");
        String path = sqlActionElement.getContainingFile().getVirtualFile().getPath();
        Map<String, Tuple3<String, String, PsiClass>> tableNameClassDetailMap = columnInfoList
            .stream()
            .map(ColumnInfo::getTableName)
            .distinct()
            .map(tableName -> {
                String entityName = AbstractDatabaseHandler.EntityDatabaseHandler.resolveEntityName(prefixes, tableName);
                PsiClass entityClass = ModuleUtils.findClassBySimpleName(module, path, entityName);
                return tuple(tableName, entityName, entityClass);
            })
            .collect(Collectors.toMap(Tuple2::_1, Function.identity()));

        Map<String, List<ColumnInfo>> tableNameColumns = columnInfoList.stream()
            .collect(Collectors.groupingBy(ColumnInfo::getTableName));

        List<ResultMapChildNode> childNodes = tableNameColumns
            .entrySet()
            .stream()
            .flatMap(
                EntryCase.match(
                    (tableName, columnInfos) -> {
                        Tuple3<String, String, PsiClass> tuple = tableNameClassDetailMap.get(tableName);
                        PsiClass entityClass = tuple._3();
                        if (Objects.nonNull(entityClass)) {
                            PsiClass mapperClass = ModuleUtils.findClassBySimpleName(module, path, entityClass.getName() + "Mapper");
                            if (Objects.nonNull(mapperClass)) {
                                AssociationNode associationNode = new AssociationNode(
                                    this,
                                    StringHelper.firstToLowerCase(entityClass.getName()),
                                    "",
                                    entityClass.getQualifiedName(),
//                                    mapperClass.getQualifiedName() + "." + "BaseResultMap"
                                    ""
                                );
                                List<AssociationNode> associationNodes = Collections.singletonList(associationNode);
                                associationNode.setChildrenNodeList(associationNode.resolveColumnInfos(project, columnInfos));
                                return associationNodes.stream();
                            }
                        }
                        return this.resolveColumnInfos(project, columnInfos).stream();
                    }
                )
            )
            .collect(Collectors.toList());


        this.childrenNodeList = childNodes;
    }

    @Override
    protected void setProperties() {
        this.properties = Lists.newArrayList(
            tuple("id", id),
            tuple("type", type)
        );
    }

    @Override
    public String getName() {
        List<Tuple2<String, String>> properties = getProperties();
        String propString = properties
            .stream()
            .map(
                TupleCase.match(
                    (key, value) -> key + '=' + '"' + value + '"'
                )
            )
            .collect(Collectors.joining(" "));
        return String.format(xmlNameBase, getTagName(), propString);
    }

    @Override
    public boolean isSingleTag() {
        return false;
    }

    @Override
    protected String getJavaTypePropertyName() {
        return "type";
    }


    public static class NormalResultMapNode extends ResultMapNode {

        protected NormalResultMapNode(List<ColumnInfo> columnInfoList, PsiElement sqlActionElement,String type) {
            super(columnInfoList, sqlActionElement,type);
        }

        @Override
        protected String getTagName() {
            return "resultMap";
        }

        @Override
        protected void init() {
            List<ResultMapChildNode> resultMapChildNodes = this.resolveColumnInfos(this.sqlActionElement.getProject(), this.columnInfoList);
            this.childrenNodeList = resultMapChildNodes;
        }
    }

    public String getClassName(){
        String type = getProperty("type");
        if (type.contains(".")){
            return type;
        }
        return StringHelper.firstToUpperCase(type);
    }
}
